package com.dingreading.cloud.admin.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dingreading.cloud.admin.dto.ShopCartSendDto;
import com.dingreading.cloud.admin.dto.StockOutDeliveryDto;
import com.dingreading.cloud.admin.entity.*;
import com.dingreading.cloud.admin.service.*;
import com.dingreading.cloud.admin.util.bean.OrderFormV3Util;
import com.dingreading.cloud.admin.util.bean.PurchaseUtil;
import com.dingreading.cloud.admin.util.redis.RedisService;
import com.dingreading.cloud.common.base.R;
import com.dingreading.cloud.common.util.*;
import com.dingreading.cloud.common.util.qyweixin.AccessTokenDto;
import com.dingreading.cloud.common.util.qyweixin.QyWeiXinUtil;
import com.dingreading.cloud.common.util.qyweixin.QyWeixinMsg;
import com.google.common.collect.Lists;
import com.mybatisflex.core.paginate.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@RefreshScope
@RestController
@RequestMapping("/system/whStockOut/v3")
@Api(tags = "总部-仓库出库管理V3")
public class WhStockOutControllerV3 {

    @Value("${serverUrl}")
    private String serverUrl;

    @Value("${qyweixin.corpid}")
    private String corpid;
    @Value("${qyweixin.secret}")
    private String secret;
    @Value("${qyweixin.agentid}")
    private String agentid;
    @Value("${qyweixin.mallchatid}")
    private String mallchatid;
    @Value("${qyweixin.thirdUserid}")
    private String thirdUserid;
//    @Value("${qyweixin.managerUserid}")
//    private String managerUserid;

    @Resource
    protected WhStockInService whStockInService;
    @Resource
    protected WhStockOutService whStockOutService;
    @Resource
    protected WhStockOutGoodsService whStockOutGoodsService;
    @Resource
    protected WhStockOutGoodsLogService whStockOutGoodsLogService;
    @Resource
    protected OrderFormService orderFormService;
    @Resource
    protected ShopCartService shopCartService;
    @Resource
    protected GoodsWetPriceService goodsWetPriceService;
    @Resource
    protected OrderFormLogService orderFormLogService;
    @Resource
    protected WhStockOutDeliveryService stockOutDeliveryService;
    @Resource
    protected WhStockOutDeliveryImgService stockOutDeliveryImgService;
    @Resource
    protected StoresService storesService;
    @Resource
    private RedisService redisService;


    @ApiOperation(value = "分页列表")
    @PostMapping(value = "/pageList", produces = "application/json; charset=UTF-8")
    public R<Object> pageList(
            @ApiParam(value = "分页公共类") PageUtil pageUtil,
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "商品订单号/出库订单号") @RequestParam(value = "keyword", required = false) String keyword,
            @ApiParam(value = "状态 -1.已退货 0.已取消 1.出库完成 2.保存") @RequestParam(value = "status", required = false) Integer status,
            @ApiParam(value = "出库方式 1.自营仓库 2.线上发货 3.第三方仓库") @RequestParam(value = "stockOutMethod", required = false) Integer stockOutMethod
    ) {
        Page<WhStockOut> page = this.whStockOutService.pageList(pageUtil, keyword, status, stockOutMethod);

        PageBeanDto data = new PageBeanDto();
        data.setTotal(page.getTotalRow());
        data.setList(page.getRecords());
        return R.ok(data);
    }


    @ApiOperation(value = "按出库方式的订单分页列表")
    @PostMapping(value = "/orderList", produces = "application/json; charset=UTF-8")
    public R<Object> orderList(
            @ApiParam(value = "分页公共类") PageUtil pageUtil,
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号/用户姓名/手机号/门店名/地址") @RequestParam(value = "keyword", required = false) String keyword,
            @ApiParam(value = "商品名称") @RequestParam(value = "goodsName", required = false) String goodsName,
            @ApiParam(value = "出库方式 1.自营仓库 2.线上发货 3.第三方仓库") @RequestParam(value = "stockOutMethod") Integer stockOutMethod,
            @ApiParam(value = "状态 0.取消 1.完成 2.待确认 3.待付款 4.待发货") @RequestParam(value = "status", required = false) Integer status
    ) {
        PageDto<OrderForm> pageList = orderFormService.pageListXml(pageUtil, keyword, stockOutMethod, status, goodsName);

        List<OrderForm> list = pageList.getList();

        JSONArray array = new JSONArray();
        if (CollectionUtils.isNotEmpty(list)) {
            List<String> codes = list.stream().map(OrderForm::getCode).collect(Collectors.toList());
            // 获取出库订单
            List<WhStockOut> stockOutList = whStockOutService.getByOrderCodesAndStockOutMethod(codes, stockOutMethod);
            Map<String, List<WhStockOut>> outMap = new HashMap<>();
            if (CollectionUtils.isNotEmpty(stockOutList)) {
                outMap = stockOutList.stream().collect(Collectors.groupingBy(WhStockOut::getOrderCode));
            }

            // 获取出库单的商品记录
            List<WhStockOutGoods> outGoodsList = whStockOutGoodsService.getOutGoodsListV2(codes, stockOutMethod);
            Map<String, List<WhStockOutGoods>> outGoodsMap = new HashMap<>();
            if (CollectionUtils.isNotEmpty(outGoodsList)) {
                outGoodsMap = outGoodsList.stream().collect(Collectors.groupingBy(WhStockOutGoods::getOrderCode));
            }

            // 获取购物车商品记录
            List<ShopCartSendDto> shopCartSendDtoList = shopCartService.listToExpress(codes, stockOutMethod);
            Map<String, List<ShopCartSendDto>> shopCartMap = new HashMap<>();
            if (CollectionUtils.isNotEmpty(shopCartSendDtoList)) {
                shopCartMap = shopCartSendDtoList.stream().collect(Collectors.groupingBy(ShopCartSendDto::getCode));
            }

            for (OrderForm orderForm : list) {
                JSONObject jsonObject = JsonUtil.toJsonObject(orderForm);
                String code = orderForm.getCode();
                List<WhStockOut> stockOuts = outMap.get(code);
                List<WhStockOutGoods> outGoods = outGoodsMap.get(code);
                // 完成出库订单数量
                int outCodeNum = 0;
                // 出库订单数量
                int outCodeCnt = 0;
                // 完成的出库商品总数量
                int outGoodsNum = 0;
                // 完成的出库商品规格数量
                int outGoodsTypeNum = 0;
                int goodsTypeNum = 0;
                int buyNum = 0;
                // 购物车商品是否全部出库完成
                boolean allOutStatus = true;
                // 更新实体商品规格数
                List<ShopCartSendDto> cartList = shopCartMap.get(code);
                // 更新实体商品总购买数
                if (CollectionUtils.isNotEmpty(cartList)) {
                    goodsTypeNum = cartList.size();
                    for (ShopCartSendDto cart : cartList) {
                        buyNum += cart.getBuyNum();
                        // 查找待发货的购物车商品记录
                        if (allOutStatus && !cart.getStatus().equals(EnumType.ShopCart.SEND.getType())) {
                            allOutStatus = false;
                        }
                    }
                }
                if (CollectionUtils.isNotEmpty(stockOuts)) {
                    outCodeCnt = stockOuts.size();
                    for (WhStockOut stockOut : stockOuts) {
                        if (stockOut.getStatus().equals(1)) {
                            outCodeNum += 1;
                        }
                    }
                }
                if (CollectionUtils.isNotEmpty(outGoods)) {
                    Set<Long> shopCartIds = new HashSet<>();
                    for (WhStockOutGoods outGood : outGoods) {
                        if (outGood.getStatus().equals(1)) {
                            outGoodsNum += NepUtil.nullToZero(outGood.getNum());
                            shopCartIds.add(outGood.getShopCartId());
                        }
                    }
                    outGoodsTypeNum = shopCartIds.size();
                }

                jsonObject.put("outCodeNum", outCodeNum);
                jsonObject.put("outCodeCnt", outCodeCnt);
                jsonObject.put("outGoodsNum", outGoodsNum);
                jsonObject.put("buyNum", buyNum);
                jsonObject.put("outGoodsTypeNum", outGoodsTypeNum);
                jsonObject.put("goodsTypeNum", goodsTypeNum);
                jsonObject.put("shopCartList", cartList);
                if (allOutStatus) {
                    // 实体商品全部出库完成，暂时修改订单为完成
                    jsonObject.put("status", EnumType.OrderForm.OK.getType());
                    jsonObject.put("statusName", EnumType.OrderForm.OK.getName());
                }
                array.add(jsonObject);
            }
        }

        PageBeanDto data = new PageBeanDto();
        data.setTotal(pageList.getTotal());
        data.setList(array);
        return R.ok(data);
    }


    @ApiOperation(value = "获取出库订单详情")
    @PostMapping(value = "/get", produces = "application/json; charset=UTF-8")
    public R<Object> get(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "出库订单号") @RequestParam(value = "outCode") String outCode
    ) throws Exception {
        WhStockOut stockOut = whStockOutService.getByOutCode(outCode);
        if (stockOut == null) return R.fail("出库记录获取失败");

        // 出库单的商品记录
        List<WhStockOutGoods> outGoodsList = whStockOutGoodsService.getByOutCode(outCode);

        // 发货记录
        List<StockOutDeliveryDto> deliveryArrays = new ArrayList<>();
        List<WhStockOutDelivery> deliveries = stockOutDeliveryService.listByOutCode(outCode);
        if (CollectionUtils.isNotEmpty(deliveries)) {
            List<Long> deliveryIdList = deliveries.stream().map(WhStockOutDelivery::getId).collect(Collectors.toList());
            List<WhStockOutDeliveryImg> deliveryImgList = stockOutDeliveryImgService.listByDeliveryIdIn(deliveryIdList);
            Map<Long, List<WhStockOutDeliveryImg>> imgMap = new HashMap<>();
            if (CollectionUtils.isNotEmpty(deliveryImgList)) {
                imgMap = deliveryImgList.stream().collect(Collectors.groupingBy(WhStockOutDeliveryImg::getDeliveryId));
            }

            for (WhStockOutDelivery delivery : deliveries) {
                StockOutDeliveryDto deliveryObj = PurchaseUtil.getDeliveryObj(delivery, imgMap.get(delivery.getId()));
                deliveryArrays.add(deliveryObj);
            }
        }

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("outData", stockOut);
        jsonObject.put("goodsList", outGoodsList);
        jsonObject.put("deliveryList", deliveryArrays);
        return R.ok(jsonObject);
    }


    @ApiOperation(value = "根据商品订单号获取出库订单列表")
    @PostMapping(value = "/getByCode", produces = "application/json; charset=UTF-8")
    public R<Object> getByCode(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "商品订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "出库方式 1.自营仓库 2.线上发货 3.第三方仓库") @RequestParam(value = "stockOutMethod") Integer stockOutMethod
    ) {
        List<WhStockOut> list = whStockOutService.getByOrderCodeAndStockOutMethod(code, stockOutMethod);
        return R.ok(list);
    }


    @ApiOperation(value = "新增/修改包裹快递信息")
    @PostMapping(value = "/newExpressInfo", produces = "application/json; charset=UTF-8")
    public R<Object> newExpressInfo(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "商品订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "快递公司") @RequestParam(value = "expressName") String expressName,
            @ApiParam(value = "快递单号") @RequestParam(value = "expressCode", required = false) String expressCode,
            @ApiParam(value = "出库订单号") @RequestParam(value = "outCode", required = false) String outCode,
            @ApiParam(value = "快递费用") @RequestParam(value = "expressPrice", required = false) Double expressPrice,
            @ApiParam(value = "揽收时间") @RequestParam(value = "expressTime", required = false) String expressTime,
            @ApiParam(value = "快递费用到付") @RequestParam(value = "expressPayType", required = false) Integer expressPayType,
            @ApiParam(value = "出库方式 1.自营仓库 2.线上发货 3.第三方仓库") @RequestParam(value = "stockOutMethod") Integer stockOutMethod,
            @ApiParam(value = "包裹数量") @RequestParam(value = "packageNum", required = false) Integer packageNum,
            @ApiParam(value = "发货仓库id") @RequestParam(value = "deliveryWarehouseId", required = false) Long deliveryWarehouseId,
            @ApiParam(value = "发货仓库名称") @RequestParam(value = "deliveryWarehouseName", required = false) String deliveryWarehouseName,
            @ApiParam(value = "物流单图片 [id:0,url:'']") @RequestParam(value = "imgJson", required = false) String imgJson,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) throws Exception {
        OrderForm form = orderFormService.getByCode(null, code);
        String resultMsg = judgeOrderFormStatus(form);
        if (StringUtils.isNotBlank(resultMsg))
            return R.fail(resultMsg);

        if (StringUtils.isBlank(outCode)) {
            // 新增
            outCode = KitUtil.getOutCode();
            // 新增出库单
            WhStockOut data = PurchaseUtil.newWhStockOut(
                    headStaffUid, headStaffName, outCode, code, 0, 0, 2, "保存",
                    form.getName(), form.getPhone(), form.getAddress(),
                    stockOutMethod, remark, 0
            );
            whStockOutService.saveOrUpdate(data);

            WhStockOutDelivery stockOutDelivery = PurchaseUtil.newStockOutDelivery(
                    null, headStaffUid, headStaffName, 1, outCode, code, stockOutMethod,
                    form.getName(), form.getPhone(), form.getAddress(),
                    expressName, expressCode, expressPrice, expressPayType, expressTime, packageNum,
                    deliveryWarehouseId, deliveryWarehouseName, remark
            );
            stockOutDeliveryService.saveOrUpdate(stockOutDelivery);

            if (StringUtils.isNotBlank(imgJson)) {
                List<WhStockOutDeliveryImg> imgList = JSONArray.parseArray(imgJson, WhStockOutDeliveryImg.class);
                if (CollectionUtils.isNotEmpty(imgList)) {
                    for (WhStockOutDeliveryImg img : imgList) {
                        img.setDeliveryId(stockOutDelivery.getId());
                    }
                    stockOutDeliveryImgService.saveOrUpdateBatch(imgList);
                }
            }
        } else {
            // 修改
            WhStockOut data = whStockOutService.getByOutCode(outCode);
            if (data == null)
                return R.fail("出库记录获取失败");
            if (!data.getStatus().equals(2))
                return R.fail("修改失败订单状态不是保存中");

            WhStockOutDelivery delivery = stockOutDeliveryService.getByOutCodeAndType(outCode, 1);
            delivery = PurchaseUtil.newStockOutDelivery(
                    delivery, headStaffUid, headStaffName, 1, outCode, code, stockOutMethod,
                    form.getName(), form.getPhone(), form.getAddress(),
                    expressName, expressCode, expressPrice, expressPayType, expressTime, packageNum,
                    deliveryWarehouseId, deliveryWarehouseName, remark
            );
            stockOutDeliveryService.saveOrUpdate(delivery);

            if (StringUtils.isNotBlank(imgJson)) {
                List<WhStockOutDeliveryImg> imgList = JSONArray.parseArray(imgJson, WhStockOutDeliveryImg.class);
                if (CollectionUtils.isNotEmpty(imgList)) {
                    List<Long> updateIdList = new ArrayList<>();
                    List<WhStockOutDeliveryImg> newList = new ArrayList<>();
                    for (WhStockOutDeliveryImg img : imgList) {
                        if (img.getId() == null || img.getId() <= 0) {
                            img.setDeliveryId(delivery.getId());
                            newList.add(img);
                        } else {
                            updateIdList.add(img.getId());
                        }
                    }

                    // 删除不要的图片
                    List<WhStockOutDeliveryImg> deleteImgList = stockOutDeliveryImgService.listByImgIdNotIn(delivery.getId(), updateIdList);
                    if (CollectionUtils.isNotEmpty(deleteImgList)) {
                        // 删除本地图片集合
                        List<String> urlList = deleteImgList.stream().map(WhStockOutDeliveryImg::getUrl).collect(Collectors.toList());
                        LocalFileUtil.deleteImgList(LocalFileUtil.orderFormBaseDir, urlList);

                        stockOutDeliveryImgService.deleteByImgIdNotIn(delivery.getId(), updateIdList);
                    }
                    // 保存
                    stockOutDeliveryImgService.saveOrUpdateBatch(newList);
                }
            } else {
                // 删除全部的图片
                List<WhStockOutDeliveryImg> deleteImgList = stockOutDeliveryImgService.listByDeliveryId(delivery.getId());
                if (CollectionUtils.isNotEmpty(deleteImgList)) {
                    // 删除本地图片集合
                    List<String> urlList = deleteImgList.stream().map(WhStockOutDeliveryImg::getUrl).collect(Collectors.toList());
                    LocalFileUtil.deleteImgList(LocalFileUtil.orderFormBaseDir, urlList);

                    stockOutDeliveryImgService.deleteByDeliveryId(delivery.getId());
                }
            }
        }
        return R.ok(outCode);
    }


    @ApiOperation(value = "新增/修改包裹商品")
    @PostMapping(value = "/newGoodsInfo", produces = "application/json; charset=UTF-8")
    public R<Object> newGoodsInfo(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "商品订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "发货的商品{shopCartId:'0',goodsId,0,goodsName:'',goodsTypeId:0,goodsTypeName:'',unit:'',realStatus:0,num:0,buyNum:0}")
            @RequestParam(value = "goodsJson") String goodsJson,
            @ApiParam(value = "出库订单号") @RequestParam(value = "outCode", required = false) String outCode,
            @ApiParam(value = "出库方式 1.自营仓库 2.线上发货 3.第三方仓库") @RequestParam(value = "stockOutMethod") Integer stockOutMethod,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) throws Exception {
        if (StringUtils.isBlank(goodsJson))
            return R.fail("请先选择商品");
        WhStockOutGoods newStockOutGoods = JSONObject.parseObject(goodsJson, WhStockOutGoods.class);
        if (null == newStockOutGoods)
            return R.fail("出库商品信息解析失败");

        ShopCart shopCart = shopCartService.getByCodeAndId(code, newStockOutGoods.getShopCartId());
        if (shopCart == null)
            return R.fail("对应的购买商品记录获取失败");
        if (!shopCart.getStatus().equals(EnumType.ShopCart.PAYED.getType()))
            return R.fail("该商品不能操作状态：" + shopCart.getStatusName());
        int num = NepUtil.nullToZero(newStockOutGoods.getNum());
        if (num <= 0)
            return R.fail("商品的出库数量必须大于0");

        OrderForm form = orderFormService.getByCode(null, code);
        String resultMsg = judgeOrderFormStatus(form);
        if (StringUtils.isNotBlank(resultMsg))
            return R.fail(resultMsg);

        // 查询该商品符合出库数量的入库记录
        List<WhStockIn> inList = whStockInService.getOkGoodsTypeUsableNumLog(newStockOutGoods.getGoodsTypeId(), num);
        if (CollectionUtils.isEmpty(inList))
            return R.fail("库存数量不足，不能出库商品名称：" + newStockOutGoods.getGoodsName() + "-" + newStockOutGoods.getGoodsTypeName());

        WhStockOut data = null;
        Long deliveryWarehouseId = null;
        String deliveryWarehouseName = null;
        if (StringUtils.isNotBlank(outCode)) {
            data = whStockOutService.getByOutCode(outCode);
            if (data == null)
                return R.fail("出库记录获取失败");
            if (!data.getStatus().equals(2))
                return R.fail("操作失败订单状态不是保存中");

            data.setLastStaffUid(headStaffUid);
            data.setLastStaffName(headStaffName);

            WhStockOutDelivery stockOutDelivery = stockOutDeliveryService.getByOutCodeAndType(outCode, 1);
            if (stockOutDelivery != null) {
                deliveryWarehouseId = stockOutDelivery.getDeliveryWarehouseId();
                deliveryWarehouseName = stockOutDelivery.getDeliveryWarehouseName();
            }
        } else {
            outCode = KitUtil.getOutCode();
            // 新增出库单
            data = PurchaseUtil.newWhStockOut(
                    headStaffUid, headStaffName, outCode, code, null, null,
                    2, "保存", form.getName(), form.getPhone(), form.getAddress(),
                    stockOutMethod, remark, 0
            );
        }
        whStockOutService.saveOrUpdate(data);

        if (null == newStockOutGoods.getId() || newStockOutGoods.getId() <= 0) {
            long duplicateShopCartId = whStockOutGoodsService.getDuplicateShopCartId(outCode, newStockOutGoods.getShopCartId());
            if (duplicateShopCartId > 0)
                return R.fail("商品不能重复加入商品名称：" + newStockOutGoods.getGoodsName() + "-" + newStockOutGoods.getGoodsTypeName());
            // 新增出库的商品记录
            newStockOutGoods.setOutCode(outCode);
            newStockOutGoods.setOrderCode(code);
            newStockOutGoods.setCreatorUid(headStaffUid);
            newStockOutGoods.setCreator(headStaffName);
            newStockOutGoods.setStockOutMethod(stockOutMethod);
            newStockOutGoods.setStatus(2);
            newStockOutGoods.setStatusName("保存");
        }
        newStockOutGoods.setDeliveryWarehouseId(deliveryWarehouseId);
        newStockOutGoods.setDeliveryWarehouseName(deliveryWarehouseName);
        newStockOutGoods.setLastStaffUid(headStaffUid);
        newStockOutGoods.setLastStaffName(headStaffName);
        whStockOutGoodsService.saveOrUpdate(newStockOutGoods);

        // 更新出库单的商品规格总数和出库总数
        List<WhStockOutGoods> outGoodsList = whStockOutGoodsService.getByOutCode(outCode);
        if (CollectionUtils.isEmpty(outGoodsList))
            return R.fail("获取出库记录失败");
        Set<Long> shopCartIds = new HashSet<>();
        int numCnt = 0;
        for (WhStockOutGoods whStockOutGoods : outGoodsList) {
            shopCartIds.add(whStockOutGoods.getShopCartId());
            numCnt += NepUtil.nullToZero(whStockOutGoods.getNum());
        }
        whStockOutService.updateGoodsTypeNumAndNum(outCode, shopCartIds.size(), numCnt);
        return R.ok(outCode);
    }


    @ApiOperation(value = "新增/修改包裹快递信息（手动添加出库单时使用）")
    @PostMapping(value = "/manual/newExpressInfo", produces = "application/json; charset=UTF-8")
    public R<Object> manualNewExpressInfo(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "商品订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "快递公司") @RequestParam(value = "expressName") String expressName,
            @ApiParam(value = "快递单号") @RequestParam(value = "expressCode", required = false) String expressCode,
            @ApiParam(value = "出库订单号") @RequestParam(value = "outCode", required = false) String outCode,
            @ApiParam(value = "快递费用") @RequestParam(value = "expressPrice", required = false) Double expressPrice,
            @ApiParam(value = "揽收时间") @RequestParam(value = "expressTime", required = false) String expressTime,
            @ApiParam(value = "快递费用到付") @RequestParam(value = "expressPayType", required = false) Integer expressPayType,
            @ApiParam(value = "包裹数量") @RequestParam(value = "packageNum", required = false) Integer packageNum,
            @ApiParam(value = "出库方式 1.自营仓库 2.线上发货 3.第三方仓库") @RequestParam(value = "stockOutMethod") Integer stockOutMethod,
            @ApiParam(value = "发货仓库id") @RequestParam(value = "deliveryWarehouseId", required = false) Long deliveryWarehouseId,
            @ApiParam(value = "发货仓库名称") @RequestParam(value = "deliveryWarehouseName", required = false) String deliveryWarehouseName,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) throws Exception {
        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null) return R.fail("订单获取失败");

        if (StringUtils.isBlank(outCode)) {
            // 新增
            outCode = KitUtil.getOutCode();
            // 新增出库单
            WhStockOut data = PurchaseUtil.newWhStockOut(
                    headStaffUid, headStaffName, outCode, code, 0, 0, 2, "保存",
                    form.getName(), form.getPhone(), form.getAddress(), stockOutMethod, remark, 1
            );
            whStockOutService.saveOrUpdate(data);

            WhStockOutDelivery stockOutDelivery = PurchaseUtil.newStockOutDelivery(
                    null, headStaffUid, headStaffName, 1, outCode, code, stockOutMethod,
                    form.getName(), form.getPhone(), form.getAddress(),
                    expressName, expressCode, expressPrice, expressPayType, expressTime, packageNum,
                    deliveryWarehouseId, deliveryWarehouseName, remark
            );
            stockOutDeliveryService.saveOrUpdate(stockOutDelivery);
        } else {
            // 修改
            WhStockOut data = whStockOutService.getByOutCode(outCode);
            if (data == null)
                return R.fail("出库记录获取失败");
            if (!data.getStatus().equals(2))
                return R.fail("修改失败订单状态不是保存中");

            WhStockOutDelivery stockOutDelivery = stockOutDeliveryService.getByOutCodeAndType(outCode, 1);
            stockOutDelivery = PurchaseUtil.newStockOutDelivery(
                    stockOutDelivery, headStaffUid, headStaffName, 1, outCode, code, stockOutMethod,
                    form.getName(), form.getPhone(), form.getAddress(),
                    expressName, expressCode, expressPrice, expressPayType, expressTime, packageNum,
                    deliveryWarehouseId, deliveryWarehouseName, remark
            );
            stockOutDeliveryService.saveOrUpdate(stockOutDelivery);
        }
        return R.ok(outCode);
    }


    @ApiOperation(value = "新增/修改包裹商品（手动添加出库单时使用）")
    @PostMapping(value = "/manual/newGoodsInfo", produces = "application/json; charset=UTF-8")
    public R<Object> manualNewGoodsInfo(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "商品订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "发货的商品{shopCartId:'0',goodsId,0,goodsName:'',goodsTypeId:0,goodsTypeName:'',unit:''," +
                    "realStatus:0,num:0,buyNum:0}") @RequestParam(value = "goodsJson") String goodsJson,
            @ApiParam(value = "出库订单号") @RequestParam(value = "outCode", required = false) String outCode,
            @ApiParam(value = "出库方式 1.自营仓库 2.线上发货 3.第三方仓库") @RequestParam(value = "stockOutMethod") Integer stockOutMethod,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) throws Exception {
        if (StringUtils.isBlank(goodsJson)) return R.fail("请先选择商品");
        WhStockOutGoods newOutGoods = JSONObject.parseObject(goodsJson, WhStockOutGoods.class);
        if (newOutGoods == null) return R.fail("商品信息解析不存在");

        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null) return R.fail("订单获取失败");

        int num = NepUtil.nullToZero(newOutGoods.getNum());
        if (num <= 0) return R.fail("商品的出库数量必须大于0");

        // 查询该商品符合出库数量的入库记录
        List<WhStockIn> inList = whStockInService.getOkGoodsTypeUsableNumLog(newOutGoods.getGoodsTypeId(), num);
        if (CollectionUtils.isEmpty(inList))
            return R.fail("库存数量不足，不能出库商品名称：" + newOutGoods.getGoodsName() + "-" + newOutGoods.getGoodsTypeName());

        WhStockOut data = null;
        Long deliveryWarehouseId = null;
        String deliveryWarehouseName = null;
        if (StringUtils.isNotBlank(outCode)) {
            data = whStockOutService.getByOutCode(outCode);
            if (data == null)
                return R.fail("出库记录获取失败");
            if (!data.getStatus().equals(2))
                return R.fail("操作失败订单状态不是保存中");
            data.setLastStaffUid(headStaffUid);
            data.setLastStaffName(headStaffName);

            WhStockOutDelivery stockOutDelivery = stockOutDeliveryService.getByOutCodeAndType(outCode, 1);
            if (stockOutDelivery != null) {
                deliveryWarehouseId = stockOutDelivery.getDeliveryWarehouseId();
                deliveryWarehouseName = stockOutDelivery.getDeliveryWarehouseName();
            }
        } else {
            outCode = KitUtil.getOutCode();
            // 新增出库单
            data = PurchaseUtil.newWhStockOut(
                    headStaffUid, headStaffName, outCode, code, null, null,
                    2, "保存", form.getName(), form.getPhone(), form.getAddress(),
                    stockOutMethod, remark, 1
            );
        }
        whStockOutService.saveOrUpdate(data);

        if (newOutGoods.getId() == null || newOutGoods.getId() <= 0) {
            long duplicateShopCartId = whStockOutGoodsService.getDuplicateShopCartId(outCode, newOutGoods.getShopCartId());
            if (duplicateShopCartId > 0)
                return R.fail("商品不能重复加入商品名称：" + newOutGoods.getGoodsName() + "-" + newOutGoods.getGoodsTypeName());
            // 新增出库的商品记录
            newOutGoods.setOutCode(outCode);
            newOutGoods.setOrderCode(code);
            newOutGoods.setCreatorUid(headStaffUid);
            newOutGoods.setCreator(headStaffName);
            newOutGoods.setStockOutMethod(stockOutMethod);
            newOutGoods.setStatus(2);
            newOutGoods.setStatusName("保存");
        }
        newOutGoods.setDeliveryWarehouseId(deliveryWarehouseId);
        newOutGoods.setDeliveryWarehouseName(deliveryWarehouseName);
        newOutGoods.setLastStaffUid(headStaffUid);
        newOutGoods.setLastStaffName(headStaffName);
        whStockOutGoodsService.saveOrUpdate(newOutGoods);

        // 更新出库单的商品规格总数和出库总数
        List<WhStockOutGoods> outGoodsList = whStockOutGoodsService.getByOutCode(outCode);
        if (CollectionUtils.isEmpty(outGoodsList)) return R.fail("获取出库记录失败");
        Set<Long> shopCartIds = new HashSet<>();
        int numCnt = 0;
        for (WhStockOutGoods whStockOutGoods : outGoodsList) {
            shopCartIds.add(whStockOutGoods.getShopCartId());
            numCnt += NepUtil.nullToZero(whStockOutGoods.getNum());
        }
        whStockOutService.updateGoodsTypeNumAndNum(outCode, shopCartIds.size(), numCnt);
        return R.ok(outCode);
    }


    @ApiOperation(value = "取消出库订单")
    @PostMapping(value = "/cancel", produces = "application/json; charset=UTF-8")
    public R<Object> cancel(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "出库订单号") @RequestParam(value = "outCode") String outCode
    ) {
        WhStockOut data = whStockOutService.getByOutCode(outCode);
        if (null == data)
            return R.fail("出库记录获取失败");
        if (!data.getStatus().equals(2))
            return R.fail("只能取消保存中的出库订单");

        boolean cancel = whStockOutService.cancel(outCode, headStaffUid, headStaffName);
        if (!cancel)
            return R.fail("取消失败");
        whStockOutGoodsService.cancel(outCode, headStaffUid, headStaffName);

        return R.ok();
    }


    @ApiOperation(value = "删除出库订单")
    @PostMapping(value = "/dropBean", produces = "application/json; charset=UTF-8")
    public R<Object> dropBean(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "出库订单号") @RequestParam(value = "outCode") String outCode
    ) {
        WhStockOut data = whStockOutService.getByOutCode(outCode);
        if (null == data)
            return R.fail("出库记录获取失败");
        if (!data.getStatus().equals(2))
            return R.fail("只能删除保存中的出库订单");

        long l = whStockOutService.dropByOutCode(outCode);
        if (l <= 0)
            return R.failDel();
        // 删除商品出库记录
        whStockOutGoodsService.dropByOutCode(outCode);
        // 删除发货记录
        stockOutDeliveryService.dropByOutCode(outCode);

        return R.ok();
    }


    @ApiOperation(value = "验证出库订单")
    @PostMapping(value = "/authOutCode", produces = "application/json; charset=UTF-8")
    public R<Object> authOutCode(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "出库订单号") @RequestParam(value = "outCode") String outCode
    ) {
        WhStockOut data = whStockOutService.getByOutCode(outCode);
        if (data == null) return R.fail("出库订单获取失败");
        if (!data.getStatus().equals(2))
            return R.fail("出库订单状态不是保存中");
        if (null == data.getStockOutMethod())
            return R.fail("出库方式错误，不能为空值");

        WhStockOutDelivery stockOutDelivery = stockOutDeliveryService.getByOutCodeAndType(outCode, 1);
        if (stockOutDelivery == null)
            return R.fail("请填写快递信息");
        if (null == stockOutDelivery.getExpressPayType())
            return R.fail("请选择快递费用类型");
        if (stockOutDelivery.getStockOutMethod().equals(3)
                && (null == stockOutDelivery.getDeliveryWarehouseId() || StringUtils.isBlank(stockOutDelivery.getDeliveryWarehouseName())))
            return R.fail("请去选择发货仓库");

        OrderForm form = orderFormService.getByCode(null, data.getOrderCode());
        String resultMsg = judgeOrderFormStatus(form);
        if (StringUtils.isNotBlank(resultMsg))
            return R.fail(resultMsg);

        List<WhStockOutGoods> stockOutGoodsList = whStockOutGoodsService.getByOutCode(outCode);
        if (CollectionUtils.isEmpty(stockOutGoodsList))
            return R.fail("出库商品获取失败");

        Set<Long> goodsTypeIds = new HashSet<>();
        // 统计商品规格的出库数量
        Map<Long, Integer> goodsTypeOutNumMap = new HashMap<>();
        for (WhStockOutGoods stockOutGoods : stockOutGoodsList) {
            Long goodsTypeId = stockOutGoods.getGoodsTypeId();
            goodsTypeIds.add(goodsTypeId);

            Integer integer = goodsTypeOutNumMap.get(goodsTypeId);
            if (integer == null)
                goodsTypeOutNumMap.put(goodsTypeId, stockOutGoods.getNum());
            else
                goodsTypeOutNumMap.put(goodsTypeId, stockOutGoods.getNum() + NepUtil.nullToZero(integer));
        }

        ArrayList<Long> goodsTypeIdList = new ArrayList<>(goodsTypeIds);
        // 查询该商品规格的库存数量
        Map<Long, Integer> stockNumMap = whStockInService.getStockNumMap(goodsTypeIdList);
        // 获取已完成出库的出库商品数量
        Map<Long, Integer> okOutNumMap = whStockOutGoodsService.getShopCartOutNum(data.getOrderCode(), 1, data.getStockOutMethod());

        boolean usableStatus = false;
        for (Map.Entry<Long, Integer> entry : goodsTypeOutNumMap.entrySet()) {
            Long goodsTypeId = entry.getKey();
            Integer outNum = entry.getValue();

            Integer stockNum = stockNumMap.get(goodsTypeId);
            // 商品出库数量大于库存数量
            if (outNum > stockNum)
                usableStatus = true;
        }

        JSONArray array = new JSONArray();
        for (WhStockOutGoods outGoods : stockOutGoodsList) {
            JSONObject jsonObject = JsonUtil.toJsonObject(outGoods);
            Long goodsTypeId = outGoods.getGoodsTypeId();
            // 查询该商品规格的剩余库存数量
            int stockNum = NepUtil.nullToZero(stockNumMap.get(goodsTypeId));
            // 已出库数量
            Integer outNum = NepUtil.nullToZero(okOutNumMap.get(outGoods.getShopCartId()));
            // 剩余的库存数量
            jsonObject.put("usableNum", stockNum);
            // 已完成的出库数
            jsonObject.put("outNum", outNum);
            array.add(jsonObject);
        }

        if (usableStatus) {
            return R.fail(4001, "商品库存不足", array);
        } else {
            return R.ok(array);
        }
    }


    @ApiOperation(value = "设置出库订单为已完成")
    @PostMapping(value = "/setOutOk", produces = "application/json; charset=UTF-8")
    public R<Object> setOutOk(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "出库订单号") @RequestParam(value = "outCode") String outCode,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) throws Exception {
        WhStockOut data = whStockOutService.getByOutCode(outCode);
        if (data == null)
            return R.fail("出库订单获取失败");
        if (!data.getStatus().equals(2))
            return R.fail("出库失败订单状态不是保存中");

        OrderForm form = orderFormService.getByCode(null, data.getOrderCode());
        String resultMsg = judgeOrderFormStatus(form);
        if (StringUtils.isNotBlank(resultMsg))
            return R.fail(resultMsg);

        Stores stores = storesService.getByUid(form.getStoreUid());
        if (null == stores)
            return R.fail("门店信息获取失败");

        List<WhStockOutGoods> stockOutGoodsList = whStockOutGoodsService.getByOutCode(outCode);
        if (CollectionUtils.isEmpty(stockOutGoodsList))
            return R.fail("出库的商品数据获取失败");
        List<Long> goodsTypeIds = stockOutGoodsList.stream().map(WhStockOutGoods::getGoodsTypeId).collect(Collectors.toList());
        // 按商品规格id分类
        Map<Long, List<WhStockOutGoods>> map = stockOutGoodsList.stream().collect(Collectors.groupingBy(WhStockOutGoods::getGoodsTypeId));

        // 获取商品的加权平均价
        List<GoodsWetPrice> goodsWetPriceList = goodsWetPriceService.getByGoodsTypeId(goodsTypeIds);
        if (CollectionUtils.isEmpty(goodsWetPriceList))
            return R.fail("商品的加权平均价获取失败");
        Map<Long, GoodsWetPrice> wetAvgPriceMap = goodsWetPriceList.stream().collect(Collectors.toMap(GoodsWetPrice::getGoodsTypeId, Function.identity()));

        // 计算要消耗的入库记录，按商品规格id分类
        Map<Long, List<WhStockIn>> consumeStockInMap = new HashMap<>();

        for (Map.Entry<Long, List<WhStockOutGoods>> entry : map.entrySet()) {
            Long goodsTypeId = entry.getKey();
            List<WhStockOutGoods> list = entry.getValue();

            WhStockOutGoods goods = list.get(0);
            String goodsName = goods.getGoodsName();
            String goodsTypeName = goods.getGoodsTypeName();
            // 本次商品的购买数
            int buyNum = 0;
            // 本次商品的实际出库数
            int outNum = 0;
            for (WhStockOutGoods stockOutGoods : list) {
                buyNum += NepUtil.nullToZero(stockOutGoods.getBuyNum());
                outNum += NepUtil.nullToZero(stockOutGoods.getNum());
            }
            if (outNum <= 0)
                return R.fail("出库数必须大于0商品名称：" + goodsName + "-" + goodsTypeName);

            GoodsWetPrice goodsWetPrice = wetAvgPriceMap.get(goodsTypeId);
            if (goodsWetPrice == null)
                return R.fail("商品的加权平均价获取失败商品名称：" + goodsName + "-" + goodsTypeName);
            if (goodsWetPrice.getUsableNum() < outNum)
                return R.fail("库存数量不足，不能出库商品名称：" + goodsName + "-" + goodsTypeName);

            // 查询该商品符合出库数量的入库记录
            List<WhStockIn> stockInList = whStockInService.getOkGoodsTypeUsableNumLog(goodsTypeId, outNum);
            if (CollectionUtils.isEmpty(stockInList))
                return R.fail("库存数量不足，不能出库商品名称：" + goodsName + "-" + goodsTypeName);
            consumeStockInMap.put(goodsTypeId, stockInList);
        }

        List<WhStockOutGoodsLog> newOutGoodsLogList = new ArrayList<>();
        Map<Long, Integer> updateStockInMap = new HashMap<>();

        for (WhStockOutGoods stockOutGoods : stockOutGoodsList) {
            // 商品规格id
            Long goodsTypeId = stockOutGoods.getGoodsTypeId();
            // 获取出库商品数量可扣的入库记录
            List<WhStockIn> whStockInList = consumeStockInMap.get(goodsTypeId);
            // 本次商品的实际出库数
            int outNum = NepUtil.nullToZero(stockOutGoods.getNum());
            // 计算入库记录的可用库存数
            for (WhStockIn stockIn : whStockInList) {
                if (outNum > 0) {
                    // 商品规格的库存数量
                    int usableNum = NepUtil.nullToZero(stockIn.getUsableNum());
                    // 本次消耗的库存数量
                    int updateNum = 0;
                    if (usableNum >= outNum) {
                        // 该入库记录的可用库存数足够
                        updateNum = outNum;
                        // 更新扣除入库记录的出库数
                        outNum = 0;
                    } else {
                        // 不够，减去可用入库数得到最新的出库数
                        updateNum = usableNum;
                        // 更新扣除入库记录的出库数
                        outNum -= usableNum;
                    }

                    // 计算入库记录要消耗的数量
                    Integer integer = updateStockInMap.get(stockIn.getId());
                    if (integer == null)
                        // 存入要消耗的库存数量
                        updateStockInMap.put(stockIn.getId(), updateNum);
                    else
                        // 更新要消耗的库存数量
                        updateStockInMap.put(stockIn.getId(), updateNum + NepUtil.nullToZero(integer));

                    // 新增出库商品使用的入库记录
                    WhStockOutGoodsLog log = PurchaseUtil.newWhStockOutGoodsLog(
                            stockOutGoods, data.getOrderCode(), outCode, stockIn, headStaffUid, headStaffName, updateNum,
                            stockOutGoods.getStockOutMethod(), stockOutGoods.getDeliveryWarehouseId(), stockOutGoods.getDeliveryWarehouseName()
                    );
                    newOutGoodsLogList.add(log);
                }
            }

            GoodsWetPrice goodsWetPrice = wetAvgPriceMap.get(stockOutGoods.getGoodsTypeId());
            // 更新出库商品的状态为已完成，出库的加权平均价格
            double wetAvgPrice = NepUtil.nullToZero(goodsWetPrice.getWetAvePrice());
            stockOutGoods.setStatus(1);
            stockOutGoods.setStatusName("出库完成");
            stockOutGoods.setWetAvgPrice(wetAvgPrice);
            stockOutGoods.setLastStaffUid(headStaffUid);
            stockOutGoods.setLastStaffName(headStaffName);
        }

        // 更新出库单
        boolean ok = whStockOutService.ok(data.getId());
        if (!ok)
            return R.fail("出库完成设置失败");

        // 按商品规格的消耗数量更新商品规格的库存数量
        String timeStr = DateUtil.getNowDatetimeStr();
        int i = whStockInService.updateUsableNum(updateStockInMap, timeStr);
        if (i <= 0)
            throw new Exception("更新入库记录的库存数量失败");

        // 保存出库商品记录
        whStockOutGoodsService.saveOrUpdateBatch(stockOutGoodsList);
        // 保存出库商品消耗的入库记录
        whStockOutGoodsLogService.saveOrUpdateBatch(newOutGoodsLogList);
        // 更新商品的加权平均价、剩余金额、剩余数量
        // 获取商品的库存数量
        Map<Long, Integer> newStockNumMap = whStockInService.getStockNumMap(goodsTypeIds);
        for (GoodsWetPrice goodsWetPrice : goodsWetPriceList) {
            int stockNum = newStockNumMap.get(goodsWetPrice.getGoodsTypeId());
            double usablePrice = DoubleUtil.mul(stockNum, goodsWetPrice.getWetAvePrice());
            goodsWetPrice.setUsableNum(stockNum);
            goodsWetPrice.setUsablePrice(usablePrice);
        }
        goodsWetPriceService.saveOrUpdateBatch(goodsWetPriceList);
        // 商品订单状态更新为发货中
//        orderFormService.updateSending(form.getCode());

        // 出库方式 1.自营仓库 2.线上发货 3.第三方仓库
        String stockOutMethodType = "";
        if (data.getStockOutMethod().equals(1)) {
            stockOutMethodType = "自营仓库";
        } else if (data.getStockOutMethod().equals(2)) {
            stockOutMethodType = "线上发货";
        } else if (data.getStockOutMethod().equals(3)) {
            stockOutMethodType = "第三方仓库";
        }

        // 企业微信获取access_token
        String accessToken = (String) redisService.get(RedisParam.QYWEINXIN_ACCESS_TOKEN);
        if (StringUtils.isBlank(accessToken)) {
            AccessTokenDto dto = QyWeiXinUtil.getAccessToken(corpid, secret);
            if (dto != null) {
                accessToken = dto.getAccess_token();
                int expires_in = dto.getExpires_in();
                // redis缓存accessToken
                redisService.set(RedisParam.QYWEINXIN_ACCESS_TOKEN, accessToken, expires_in);
            }
        }
        // 企业微信发送群聊消息
        if (form.getProjectType().equals(EnumType.BalanceProject.BUY_GOODS.getType())) {
            if (AccountUtil.isJointOperation(stores.getOperationType(), form.getStaffUid())) {
                QyWeiXinUtil.sendToUser(accessToken, agentid, thirdUserid,
                        QyWeixinMsg.goodsDeliveryMsg(serverUrl, form.getStoreName(), form.getCode(), form.getProjectType(), stockOutMethodType, timeStr, "/admin/purchaseFreeOrder?keyword="));
            } else if (AccountUtil.isSelfStore(stores.getOperationType())) {
//                QyWeiXinUtil.sendToUser(accessToken, agentid, managerUserid,
//                        QyWeixinMsg.goodsDeliveryMsg(serverUrl, form.getStoreName(), form.getCode(), form.getProjectType(), stockOutMethodType, timeStr, "/admin/freeOrder?keyword="));
//                QyWeiXinUtil.sendToUser(accessToken, agentid, subManagerUserid,
//                        QyWeixinMsg.goodsDeliveryMsg(serverUrl, form.getStoreName(), form.getCode(), form.getProjectType(), stockOutMethodType, timeStr, "/admin/freeOrder?keyword="));
            } else {
                QyWeiXinUtil.sendToChat(accessToken, mallchatid,
                        QyWeixinMsg.goodsDeliveryMsg(serverUrl, form.getStoreName(), form.getCode(), form.getProjectType(), stockOutMethodType, timeStr, "/admin/orderManage?keyword="));
            }
        } else if (form.getProjectType().equals(EnumType.BalanceProject.GIFT_GOODS.getType())) {
            QyWeiXinUtil.sendToChat(accessToken, mallchatid,
                    QyWeixinMsg.goodsDeliveryMsg(serverUrl, form.getStoreName(), form.getCode(), form.getProjectType(), stockOutMethodType, timeStr, "/admin/giftOrder?keyword="));
        }

        return R.ok();
    }


    @ApiOperation(value = "验证商品订单")
    @PostMapping(value = "/authFrom", produces = "application/json; charset=UTF-8")
    public R<Object> authFrom(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "商品订单号") @RequestParam(value = "code") String code
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        String resultMsg = judgeOrderFormStatus(form);
        if (StringUtils.isNotBlank(resultMsg))
            return R.fail(resultMsg);

        // 获取订单的可发货商品
        List<ShopCartSendDto> shopCartList = shopCartService.listToExpress(Collections.singletonList(form.getCode()), null);
        if (CollectionUtils.isEmpty(shopCartList))
            return R.fail("购物车商品记录获取失败");

        // 获取保存中的出库订单数量
        long waitOutGoodsCnt = whStockOutService.getSaveOutCnt(form.getCode());

        // 获取订单已完成出库的商品数量
        Map<Long, Integer> okOutNumMap = whStockOutGoodsService.getShopCartOutNum(code, 1, null);
        // 获取订单待出库的商品数量
        Map<Long, Integer> waitOutNumMap = whStockOutGoodsService.getShopCartOutNum(code, 2, null);

        boolean outStatus = false;
        boolean waitStatus = false;
        for (ShopCartSendDto shopCart : shopCartList) {
            Long shopCartId = shopCart.getShopCartId();
            Integer outNum = NepUtil.nullToZero(okOutNumMap.get(shopCartId));
            Integer waitOutNum = NepUtil.nullToZero(waitOutNumMap.get(shopCartId));
            // 已出库数小于购买数
            if (outNum <= 0 || outNum < shopCart.getBuyNum()) {
                outStatus = true;
            }
            // 待出库数大于0
            if (waitOutNum > 0) {
                waitStatus = true;
            }
            shopCart.setOutNum(outNum);
            shopCart.setWaitOutNum(waitOutNum);
        }
        if (waitOutGoodsCnt > 0) {
            return R.fail(4001, "请先对保存中的出库订单进行处理", shopCartList);
        } else if (outStatus) {
            return R.fail(4001, "商品还未出库或出库数小于购买数", shopCartList);
        } else if (waitStatus) {
            return R.fail(4001, "发现待出库的商品", shopCartList);
        } else {
            return R.ok(shopCartList);
        }
    }


    @ApiOperation(value = "设置商品订单为已完成")
    @PostMapping(value = "/setFromOk", produces = "application/json; charset=UTF-8")
    public R<Object> setFromOk(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "商品订单号") @RequestParam(value = "code") String code
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        String resultMsg = judgeOrderFormStatus(form);
        if (StringUtils.isNotBlank(resultMsg))
            return R.fail(resultMsg);


        boolean l = shopCartService.setOutOk(code);
        if (!l)
            return R.fail("订单内的购物车商品设置完成失败");

        // 获取订单关联的购物车商品（实体/非实体）没有出库完成的数量
        long waitOutCnt = shopCartService.getWaitOutCnt(code);
        if (waitOutCnt == 0) {
            // 订单关联的购物车商品（实体/非实体）全部出库完成，设置订单完成
            orderFormService.setOk(code);
            // 添加订单操作记录
            OrderFormLog log = OrderFormV3Util.newHeadLog(form.getCode(), headStaffUid, headStaffName,
                    EnumType.OrderForm.OK.getType(), "订单完成", "");
            orderFormLogService.saveOrUpdate(log);
        }

        return R.ok();
    }


    @ApiOperation(value = "获取商品订单详情")
    @PostMapping(value = "/getOrderForm", produces = "application/json; charset=UTF-8")
    public R<Object> getOrderForm(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "商品订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "出库方式 1.自营仓库 2.线上发货 3.第三方仓库") @RequestParam(value = "stockOutMethod", required = false) Integer stockOutMethod
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null)
            return R.fail("订单获取失败");

        List<ShopCartSendDto> shopCartList = shopCartService.listToExpress(Lists.newArrayList(form.getCode()), stockOutMethod);
        if (CollectionUtils.isEmpty(shopCartList))
            return R.fail("购物车商品记录获取失败");

        // 获取已完成出库的出库订单商品数量
        Map<Long, Integer> outNumMap = whStockOutGoodsService.getShopCartOutNum(code, 1, stockOutMethod);
        // 获取保存中的出库订单商品数量
        Map<Long, Integer> waitOutNumMap = whStockOutGoodsService.getShopCartOutNum(code, 2, stockOutMethod);

        // 查询商品的库存数量
        List<Long> goodsTypeIds = shopCartList.stream().map(ShopCartSendDto::getGoodsTypeId).collect(Collectors.toList());
        Map<Long, Integer> stockNumMap = whStockInService.getStockNumMap(goodsTypeIds);

        // 获取购买商品对应的已完成的出库数量、待完成的出库数量、可售数量
        setWaitNumAndOutNumAndStockNum(outNumMap, waitOutNumMap, stockNumMap, shopCartList);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("phone", form.getPhone());
        jsonObject.put("name", form.getName());
        jsonObject.put("address", form.getAddress());
        jsonObject.put("price", form.getPrice());
        jsonObject.put("goodsList", shopCartList);
        return R.ok(jsonObject);
    }


    @ApiOperation(value = "打印商品订单详情")
    @PostMapping(value = "/printOrderForm", produces = "application/json; charset=UTF-8")
    public R<Object> printOrderForm(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "商品订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "出库方式 1.自营仓库 2.线上发货 3.第三方仓库") @RequestParam(value = "stockOutMethod", required = false) Integer stockOutMethod
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null)
            return R.fail("订单获取失败");

        List<ShopCartSendDto> shopCartList = shopCartService.listToExpress(Lists.newArrayList(form.getCode()), stockOutMethod);
        if (CollectionUtils.isEmpty(shopCartList))
            return R.fail("购物车商品记录获取失败");

        // 获取已完成出库的出库订单商品数量
        Map<Long, Integer> outNumMap = whStockOutGoodsService.getShopCartOutNum(code, 1, stockOutMethod);
        // 获取保存中的出库订单商品数量
        Map<Long, Integer> waitOutNumMap = whStockOutGoodsService.getShopCartOutNum(code, 2, stockOutMethod);

        // 查询商品的库存数量
        List<Long> goodsTypeIds = shopCartList.stream().map(ShopCartSendDto::getGoodsTypeId).collect(Collectors.toList());
        Map<Long, Integer> stockNumMap = whStockInService.getStockNumMap(goodsTypeIds);

        // 按包邮状态分组
        LinkedHashMap<Integer, List<ShopCartSendDto>> cartMap = shopCartList.stream()
                .collect(Collectors.groupingBy(shopCartSendDto -> {
                    if (shopCartSendDto.getFreeDeliveryStatus() == null || shopCartSendDto.getFreeDeliveryStatus().equals(0))
                        return 0;
                    else
                        return 1;
                }, LinkedHashMap::new, Collectors.toList()));

        // 不包邮商品
        List<ShopCartSendDto> notFreeDeliveryList = cartMap.get(0);
        // 获取购买商品对应的已完成的出库数量、待完成的出库数量、可售数量
        setWaitNumAndOutNumAndStockNum(outNumMap, waitOutNumMap, stockNumMap, notFreeDeliveryList);

        // 包邮商品
        List<ShopCartSendDto> freeDeliveryList = cartMap.get(1);
        // 获取购买商品对应的已完成的出库数量、待完成的出库数量、可售数量
        setWaitNumAndOutNumAndStockNum(outNumMap, waitOutNumMap, stockNumMap, freeDeliveryList);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("phone", form.getPhone());
        jsonObject.put("name", form.getName());
        jsonObject.put("address", form.getAddress());
        jsonObject.put("price", form.getPrice());
        jsonObject.put("notFreeDeliveryList", notFreeDeliveryList);
        jsonObject.put("freeDeliveryList", freeDeliveryList);
        return R.ok(jsonObject);
    }


    @ApiOperation(value = "上传物流单截图")
    @PostMapping(value = "/uploadDeliveryImg", produces = "application/json; charset=UTF-8")
    public R<Object> uploadDeliveryImg(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "图片文件") @RequestPart(value = "file") MultipartFile file
    ) throws Exception {
        // 上传图片到服务器
        R<String> r = LocalFileUtil.uploadImg(LocalFileUtil.orderFormBaseDir, file);
        if (R.isError(r))
            return R.fail(r.getMsg());

        String imageUrl = r.getData();

        return R.ok(imageUrl);
    }


    /**
     * 判断订单状态和关联状态
     */
    private String judgeOrderFormStatus(OrderForm form) {
        if (form == null)
            return "订单获取失败";
        if (!form.getStatus().equals(EnumType.OrderForm.WAIT_SEND.getType()) && !form.getStatus().equals(EnumType.OrderForm.SENDING.getType()))
            return "商品订单状态不符合订单状态：" + EnumType.OrderForm.getStatus(form.getStatus());
        if (form.getPayChannelCode().equals(EnumType.PayChannelCode.BANK.getType()) && form.getFinanceStatus() != null
                && form.getFinanceStatus().equals(EnumType.OrderForm.FINANCE_CHECK_REFUSE.getType()))
            return "商品订单被财务审核拒绝";
        return "";
    }

    /**
     * 获取购买商品对应的已完成的出库数量、待完成的出库数量、可售数量
     */
    private void setWaitNumAndOutNumAndStockNum(
            Map<Long, Integer> outNumMap, Map<Long, Integer> waitOutNumMap, Map<Long, Integer> stockNumMap,
            List<ShopCartSendDto> cartList
    ) {
        if (CollectionUtils.isNotEmpty(cartList)) {
            for (ShopCartSendDto cart : cartList) {
                Long shopCartId = cart.getShopCartId();
                Long goodsTypeId = cart.getGoodsTypeId();
                Integer okOutNum = NepUtil.nullToZero(outNumMap.get(shopCartId));
                Integer waitOutNum = NepUtil.nullToZero(waitOutNumMap.get(goodsTypeId));
                Integer stockNum = NepUtil.nullToZero(stockNumMap.get(goodsTypeId));

                cart.setOutNum(okOutNum);
                cart.setWaitOutNum(waitOutNum);
                cart.setUsableNum(stockNum);
            }
        }
    }

}